// call的实现
/**
  - 第一个参数为 null 或者 undefined 时，this指向全局对象window，值为原始值的指向该原始值的自动包装对象，如 String、Number、Boolean
  - 为了避免函数名与上下文（context）的属性发生冲突，使用Symbol类型作为唯一值
  - 将函数作为传入的上下文（context）属性执行
  - 函数执行完成后删除该属性
  - 返回执行结果
 */

Function.prototype.myCall = function(context, ...args) {
  const cxt = context || window
  const func = Symbol()
  // 将当前被调用的方法定义在cxt.func上（为了能以对象调用形式绑定this）
  // 新建一个唯一的Symbol变量避免重复
  cxt[func] = this
  args = args ? args : []
  // 以对象调用形式调用func，此时this指向cxt 也就是传入的需要绑定的this指向

  const res = args.length > 0 ? cxt[func](...args) : cxt[func]()
  
  // 删除该方法，不然会对传入对象造成污染（添加该方法）
  delete cxt[func]
  return res
}

Function.prototype.myApply = function(context, args = []) {
  let cxt = context || window
  let func = Symbol()
  cxt[func] = this

  const res = args.length > 0 ? cxt[func](...args) : cxt[func]()
  delete cxt[func]
  return res
}

Function.prototype.myBind = function(context, ...args) {
  const fn = this
  args = args ? args : []
  return function newFn(...newFnArgs) {
    if (this instanceof newFn) {
      return new fn(...args, ...newFnArgs)
    }
    return fn.apply(context, [...args, ...newFnArgs])
  }
}

let name = '小王', age = 17
let obj = {
  name: '小张',
  age: this.age,
  myFun: function(from, to) {
    console.log(this.name + ' 年龄 ' + this.age + '来自 ' + from + '去往' + to)
  }
}
let db = {
  name: '德玛',
  age: 99
}

// 结果
// obj.myFun.myCall(db, '成都', '上海')
// obj.myFun.myApply(db, ['成都', '上海'])
// obj.myFun.myBind(db, '成都', '上海')()

// 高级：支持 new， 例如 new (funcA.bind(thisArg), args))
function bind_3(asThis) {
  var slice = Array.prototype.slice
  var args1 = slice.call(arguments, 1);
  var fn = this;
  if (typeof fn !== "function") {
    throw new Error("Must accept function")
  }
  function resultFn() {
    var args2 = slice.call(arguments, 0);
    return fn.apply(
      resultFn.prototype.isPrototypeOf(this) ? this : asThis, // 用来绑定 this
      args1.concat(args2)
    )
    resultFn.prototype = fn.prototype
    return resultFn
  }
}


function fn (target) {
  let ret = Object(target)
  return ret
}
var a = {
  a: 1
}

var b = fn(a)
b.a = 2
console.log(a, '1111111111111')